/**
 * create by zhangpu
 * date:2015年3月2日
 */
package com.acooly.module.openapi.client.provider.bosc;

import com.acooly.core.utils.FreeMarkers;
import com.acooly.core.utils.net.HttpResult;
import com.acooly.core.utils.net.Https;
import com.acooly.module.openapi.client.api.AbstractApiServiceClient;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.exception.ApiServerException;
import com.acooly.module.openapi.client.api.marshal.ApiMarshal;
import com.acooly.module.openapi.client.api.marshal.ApiUnmarshal;
import com.acooly.module.openapi.client.api.message.PostRedirect;
import com.acooly.module.openapi.client.api.transport.Transport;
import com.acooly.module.openapi.client.provider.bosc.domain.BoscNotify;
import com.acooly.module.openapi.client.provider.bosc.domain.BoscRequest;
import com.acooly.module.openapi.client.provider.bosc.domain.BoscResponse;
import com.acooly.module.openapi.client.provider.bosc.enums.BoscCodeEnum;
import com.acooly.module.openapi.client.provider.bosc.enums.BoscStatusEnum;
import com.acooly.module.openapi.client.provider.bosc.marshall.*;
import com.acooly.module.openapi.client.provider.bosc.message.fund.DownloadCheckFileRequest;
import com.acooly.module.openapi.client.provider.bosc.message.fund.DownloadCheckFileResponse;
import com.acooly.module.openapi.client.provider.bosc.message.fund.QueryTransactionRequest;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.Map;

/**
 * 上海银行P2P存管ApiService执行器  外部业务禁止使用
 *
 * @author zhangpu
 */
@Slf4j
@Component("boscApiServiceClient")
public class BoscApiServiceClient
        extends AbstractApiServiceClient<BoscRequest, BoscResponse, BoscNotify, BoscNotify> {


    @Resource(name = "boscHttpTransport")
    private Transport transport;
    @Resource(name = "boscRequestMarshall")
    private BoscRequestMarshall requestMarshal;
    @Resource(name = "boscRedirectMarshall")
    private BoscRedirectMarshall redirectMarshal;
    @Resource(name = "boscResponseUnmarshall")
    private BoscResponseUnmarshall responseUnmarshal;
    @Resource(name = "boscNotifyUnmarshall")
    private BoscNotifyUnmarshall notifyUnmarshal;
    @Resource(name = "boscRedirectPostMarshall")
    private BoscRedirectPostMarshall boscRedirectPostMarshall;

    @Autowired
    private OpenAPIClientBoscProperties openAPIClientBoscProperties;

    /**
     * 同步请求
     *
     * @param request
     * @return
     */
    @Override
    public BoscResponse execute(BoscRequest request) {
        try {
            beforeExecute(request);
            String url = openAPIClientBoscProperties.getServiceUrl();
            String requestMessage = getRequestMarshal().marshal(request);
            HttpResult result = getTransport().request(requestMessage, url);
            BoscResponse t = this.responseUnmarshal.unmarshal(result, request.getService());
            afterExecute(t);
            return t;
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }


    /**
     * 交易查询接口
     *
     * @param request
     * @return
     */
    public BoscResponse executeTransactionQuery(QueryTransactionRequest request) {
        try {
            beforeExecute(request);
            String url = openAPIClientBoscProperties.getServiceUrl();
            String requestMessage = getRequestMarshal().marshal(request);
            HttpResult result = getTransport().request(requestMessage, url);
            BoscResponse t = this.responseUnmarshal.unmarshal(result, request.getService() + "_" + request.getTransactionType());
            afterExecute(t);
            return t;
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }


    /**
     * 文件下载
     *
     * @param request
     * @return
     */
    public DownloadCheckFileResponse downloadFile(DownloadCheckFileRequest request) {

        InputStream in = null;
        FileOutputStream out = null;
        try {
            DownloadCheckFileResponse response = new DownloadCheckFileResponse();
            beforeExecute(request);
            String url = openAPIClientBoscProperties.getDownloadUrl();
            String requestMessage = getRequestMarshal().marshal(request);

            HttpPost post = new HttpPost(url);
            post.setEntity(new StringEntity(requestMessage, (ContentType) null));

            Https.HttpResultEx result = Https
                    .getInstance().readTimeout(60000).execute(buildHttpPost(url, requestMessage, loadContentType(null)), null, "utf-8");

            if (result.getContent().available() <= 0) {
                response.setStatus(BoscStatusEnum.FAIL);
                response.setErrorMessage("响应流长度为0,文件下载失败.");
            }

            File checkFile = new File(request.getFilePath() + File.separator + request.getFileDate() + "_" +
                    DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + ".zip");

            File parentFile = checkFile.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }

            if (checkFile.createNewFile()) {
                out = FileUtils.openOutputStream(checkFile);
                IOUtils.copy(result.getContent(), out);
                out.flush();
                response.setFileName(checkFile.getName());
                response.setCode(BoscCodeEnum.SUCCESS.code());
                response.setStatus(BoscStatusEnum.SUCCESS);
                log.info("对账文件下载成功：对账日期 :{} 文件大小: {} bytes 文件存储路径 :{}", request.getFileDate(), checkFile.length(), checkFile.getCanonicalPath()
                );
            } else {
                response.setStatus(BoscStatusEnum.FAIL);
                response.setErrorMessage("本地创建对账文件失败");
            }
            return response;
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        } finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(in);
        }
    }

    private HttpPost buildHttpPost(String url, String body, ContentType contentType) {
        HttpPost post = new HttpPost(url);
        post.setEntity(new StringEntity(body, contentType));
        return post;
    }

    private ContentType loadContentType(ContentType contentType) {
        if (contentType != null) {
            return contentType;
        }
        return ContentType.create(
                ContentType.APPLICATION_FORM_URLENCODED.getMimeType(), "utf-8");
    }


    @Override
    public String redirectGet(BoscRequest request) {
        PostRedirect postRedirect = redirectPost(request);
        Map<String, Object> templateData = Maps.newHashMap();
        templateData.put("random", RandomStringUtils.randomAlphanumeric(10));
        templateData.put("redirectUrl", postRedirect.getRedirectUrl());
        templateData.put("formDatas", postRedirect.getFormDatas());
        String template =
                "<form id=\"redirectForm_${random}\" action=\"${redirectUrl}\" method=\"post\">  \n" +
                        "<#list formDatas?keys as key>\n" +
                        "    <input name=\"${key}\" value='${formDatas[key]?default(\"\")}' type='hidden'/>\n" +
                        "</#list>\n" +
                        "</form>\n" +
                        "<script>document.getElementById(\"redirectForm_${random}\").submit();</script>";
        String fromHtml = FreeMarkers.rendereString(template, templateData);
        return fromHtml;
    }

    @Override
    public PostRedirect redirectPost(BoscRequest request) {
        try {
            return boscRedirectPostMarshall.marshal(request);
        } catch (ApiServerException ose) {
            log.warn("服务器错误:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.warn("客户端异常:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.warn("内部错误:" + e.getMessage(), e);
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }


    @Override
    protected String getRedirectGateway() {
        return openAPIClientBoscProperties.getGatewayUrl();
    }

    @Override
    protected ApiMarshal<String, BoscRequest> getRequestMarshal() {
        return this.requestMarshal;
    }


    @Override
    protected ApiMarshal<String, BoscRequest> getRedirectMarshal() {
        return this.redirectMarshal;
    }

    @Override
    protected ApiUnmarshal<BoscNotify, Map<String, String>> getNoticeUnmarshal() {
        return this.notifyUnmarshal;
    }


    protected ApiUnmarshal<BoscResponse, HttpResult> getResponseUnmarshalSpec() {
        return this.responseUnmarshal;
    }

    @Override
    protected ApiUnmarshal<BoscResponse, String> getResponseUnmarshal() {
        return null;
    }

    @Override
    protected ApiUnmarshal<BoscNotify, Map<String, String>> getReturnUnmarshal() {
        return this.notifyUnmarshal;
    }

    @Override
    protected Transport getTransport() {
        return this.transport;
    }

    @Override
    public String getName() {
        return BoscConstants.PROVIDER_NAME;
    }


}
